
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//     Filename: BJXMLTranslator.cpp                                        //
//  Create date: April 1, 2004                                              //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
//                                                                          //
//  Description: This file implements the BJXMLTranslator class.  This class//
//                is a protocol translator that translates from the RTG     //
//                Black Jack protocol to XML and back.                      //
//                                                                          //
//////////////////////////////////////////////////////////////////////////////
//       Copyright (c) 2004 - Realtime Gaming.  All rights reserved.        //
//////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include <stdio.h>
#include "lib.h"
#include "BJXMLTranslator.h"
#include "XMLDoc.h"
#include "endian.h"
#include "log.h"
#include "card.h"
#include "..\BJ\BJRules.h"

/// [2013-07-09] Charles. Added for supporting 'Zappit Rackcard'.
#include "bjconn.h"

//////////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////////
BJXMLTranslator::BJXMLTranslator( Server* pServer, Connection* pParent ) : 
                                             XMLTranslator( pServer, pParent, 0, 1 )
{
}

//////////////////////////////////////////////////////////////////////////////
// Destructor
//////////////////////////////////////////////////////////////////////////////
BJXMLTranslator::~BJXMLTranslator()
{
}

//////////////////////////////////////////////////////////////////////////////
// TranslateIncomingMessage
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateIncomingMessage( uint8* pGameInBuff,  
												 uint32 nGameInBuffSize, 
												 uint8* pOutBuff, 
												 uint32 nOutBuffSize )
{
    bool8 bRetVal = false;

	// Handle the messages.  Note, I'm only going to look at the first character
	//  in a small attempt to optimize a bit
	switch( m_sMsgID[0] )
	{
		case 'L':
			bRetVal = TranslateInLogin( m_pDoc, pGameInBuff, nGameInBuffSize );
			break;

		case 'B':
			bRetVal = TranslateInBalance( m_pDoc, pGameInBuff, nGameInBuffSize, GetNumBytesMsgLen() );
			break;

		case 'D':
			if ( m_sMsgID[1] == 'e' )
			{
				bRetVal = TranslateInDeal( m_pDoc, pGameInBuff, nGameInBuffSize );
			}
			else if ( m_sMsgID[1] == 'o' )
			{
				bRetVal = TranslateInDouble( m_pDoc, pGameInBuff, nGameInBuffSize );
			}
			else
			{
				String sMsg = TEXT("Error, received unknown client message: ");
				sMsg += m_sMsgID;
				Log::msgError( sMsg );
			}
			break;

		case 'A':
			bRetVal = TranslateInAction( m_pDoc, pGameInBuff, nGameInBuffSize );
			break;

		case 'H':
			bRetVal = TranslateInHit( m_pDoc, pGameInBuff, nGameInBuffSize );
			break;

		case 'I':
			bRetVal = TranslateInInsurance( m_pDoc, pGameInBuff, nGameInBuffSize );
			break;

		case 'S':
			if ( m_sMsgID[1] == 't' )
			{
				bRetVal = TranslateInStand( m_pDoc, pGameInBuff, nGameInBuffSize );
			}
			else if ( m_sMsgID[1] == 'p' )
			{
				bRetVal = TranslateInSplit( m_pDoc, pGameInBuff, nGameInBuffSize );
			}
			else if ( m_sMsgID[1] == 'u' )
			{
				bRetVal = TranslateInSurrender( m_pDoc, pGameInBuff, nGameInBuffSize );
			}
			else
			{
				String sMsg = TEXT("Error, received unknown client message: ");
				sMsg += m_sMsgID;
				Log::msgError( sMsg );
			}
			break;

		/// [2013-07-09] Charles. Added for supporting 'Zappit Rackcard'.
		case 'Z': 
			bRetVal = TranslateInZap( m_pDoc, pGameInBuff, nGameInBuffSize );
			break;

		default:
			String sMsg = TEXT("Error, received unknown client message: ");
			sMsg += m_sMsgID;
			Log::msgError( sMsg );
			break;
	}

	// Report Error
	if( !bRetVal )
	{
		if ( m_sMsgID[0] == 'L' )
		{
			// Set an error into the out buffer
			ReturnLoginError( pOutBuff, nOutBuffSize );
		}
		else
		{
			// Set an error into the out buffer
			ReturnErrorMessage( TEXT("ServerError"), pOutBuff, nOutBuffSize );

			String sMsg = TEXT("Error translating incoming message: ");
			sMsg += m_sMsgID;
			Log::msgError( sMsg );
		}
	}

	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutgoingMessage
//////////////////////////////////////////////////////////////////////////////
uint32 BJXMLTranslator::TranslateOutgoingMessage( uint8* pOutBuff, 
												  uint32 nOutBuffSize, 
												  uint8* pGameOutBuff, 
												  uint32 nGameOutBuffSize, 
												  uint32 nTotalGameBytes    )
{
	bool8 bResult = true;
	uint32 nGameBytesUsed = 0;
	uint32 nOutBytesUsed = 0;

	while ( bResult && (nGameBytesUsed < nTotalGameBytes) )
	{
		// Keep track of buffers as we might be processing more than one game
		//   message at a time.
		uint8* pCurGameBuf     = pGameOutBuff     + nGameBytesUsed;
		uint32 nCurGameBufSize = nGameOutBuffSize - nGameBytesUsed;
		uint8* pCurOutBuf      = pOutBuff         + nOutBytesUsed;
		uint32 nCurOutBufSize  = nOutBuffSize     - nOutBytesUsed;

		// Make sure buffer has not been overrun!
		if ( nOutBytesUsed > nOutBuffSize )
		{
			ASSERT(false);
			Log::msgError( "Error, out buffer overflow!" );
			return 0;
		}

		// Now the actual message handlers!
		switch( pCurGameBuf[1] )
		{
			case 'L':
				bResult = TranslateOutLogin( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;
			
			case 'm':
				bResult = TranslateOutBalance( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize, 1 );
				break;

			case 'O':
				bResult = TranslateOutOptions( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;
			
			case 'A':
				bResult = TranslateOutAction( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;
			
			case 'H':
				bResult = TranslateOutHit( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;
			
			case 'S':
				bResult = TranslateOutStand( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;
			
			case 's':
				bResult = TranslateOutStand( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;
			
			case 'R':
				bResult = TranslateOutSurrender( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			/// [2013-07-09] Charles. Added for supporting 'Zappit Rackcard'.
			case 'Z':
				bResult = TranslateOutZap( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			case 'D':
				bResult = TranslateOutDeal( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			case 'B':
				bResult = TranslateOutDouble( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			case 'P':
				bResult = TranslateOutSplit( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			case 'I':
				bResult = TranslateOutInsurance( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			case 'X':
				bResult = TranslateOutPlayNext( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			case 'F':
				bResult = TranslateOutFinished( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			case 'E':
				bResult = TranslateGenericOutError( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize, 1 );
				break;

			case 'C':
			case 'N':
			case 'G':
			case 'b':
			case 'l':
			case '$':
			case '!':
				bResult = TranslateOutError( pCurOutBuf, nCurOutBufSize, pCurGameBuf, nCurGameBufSize );
				break;

			default:
				bResult = ReturnErrorMessage( TEXT("UnknownMessage"), pCurOutBuf, nCurOutBufSize );
				Log::msgError( "Received unknown game message: %c", pCurGameBuf[1] );
				break;
		}

		// Check and see if any errors occurred
		if ( !bResult )
		{
			ReturnErrorMessage( TEXT("ParseError"), pCurOutBuf, nCurOutBufSize );
			Log::msgError( "Error translating outgoing message: %c", pCurGameBuf[1] );
		}

		// We know that the size of the message is always stored in the first
		//  byte of the client message.
		nGameBytesUsed += pCurGameBuf[0];

		// Out XML Messages are supposed to be NULL Terminated
		nOutBytesUsed += strlen( (char*) pCurOutBuf );
		nOutBytesUsed++;  // Add one for the NULL

		DEBUGMSG( ("\n***Outgoing: %s", pCurOutBuf) );
	}

	return nOutBytesUsed;
}

//****************************************************************************
// Internal Helpers
//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
// AddOptionBits
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::AddOptionBits( XMLDoc* pDoc, uint8 nBits )
{
	ASSERT( pDoc );
	if ( !pDoc )
	{
		return false;
	}

	// Start Adding Options
	if ( !pDoc->AddRootChildNode( TEXT("bet"), (bool8) (nBits & OPTION_FLAG(Bet)) ) )
	{
		return false;
	}
	if ( !pDoc->AddRootChildNode( TEXT("hit"), (bool8) (nBits & OPTION_FLAG(Hit)) ) )
	{
		return false;
	}
	if ( !pDoc->AddRootChildNode( TEXT("stand"), (bool8) (nBits & OPTION_FLAG(Stand)) ) )
	{
		return false;
	}
	if ( !pDoc->AddRootChildNode( TEXT("split"), (bool8) (nBits & OPTION_FLAG(Split)) ) )
	{
		return false;
	}
	if ( !pDoc->AddRootChildNode( TEXT("double"), (bool8) (nBits & OPTION_FLAG(Double)) ) )
	{
		return false;
	}
	if ( !pDoc->AddRootChildNode( TEXT("surrender"), (bool8) (nBits & OPTION_FLAG(Surrender)) ) )
	{
		return false;
	}
	/// [2013-07-10] Charles. Added for supporting 'Zappit Rackcard'.
	BJConnection *conn = (BJConnection *)m_pParent;
	if(conn->IsMachine_Zappit()) {
		if ( !pDoc->AddRootChildNode( TEXT("zap"), (bool8) (nBits & OPTION_FLAG(Zap)) ) )
		{
			return false;
		}
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// AddOptionBits
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::AddOptionBits( XMLDoc* pDoc, 
									  const String& sParentQuery, 
									  uint8 nBits )
{
	ASSERT( pDoc );
	if ( !pDoc )
	{
		return false;
	}

	// Start Adding Options
	if ( !pDoc->AddChildNode( sParentQuery, TEXT("bet"), (bool8) (nBits && OPTION_FLAG(Bet)) ) )
	{
		return false;
	}
	if ( !pDoc->AddChildNode( sParentQuery, TEXT("hit"), (bool8) (nBits && OPTION_FLAG(Hit)) ) )
	{
		return false;
	}
	if ( !pDoc->AddChildNode( sParentQuery, TEXT("stand"), (bool8) (nBits && OPTION_FLAG(Stand)) ) )
	{
		return false;
	}
	if ( !pDoc->AddChildNode( sParentQuery, TEXT("split"), (bool8) (nBits && OPTION_FLAG(Split)) ) )
	{
		return false;
	}
	if ( !pDoc->AddChildNode( sParentQuery, TEXT("double"), (bool8) (nBits && OPTION_FLAG(Double)) ) )
	{
		return false;
	}
	if ( !pDoc->AddChildNode( sParentQuery, TEXT("surrender"), (bool8) (nBits && OPTION_FLAG(Surrender)) ) )
	{
		return false;
	}
	/// [2013-07-10] Charles. Added for supporting 'Zappit Rackcard'.
	BJConnection *conn = (BJConnection *)m_pParent;
	if(conn->IsMachine_Zappit()) {
		if ( !pDoc->AddChildNode( sParentQuery, TEXT("zap"), (bool8) (nBits && OPTION_FLAG(Zap)) ) )
		{
			return false;
		}
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// AddHandStructure
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::AddHandStructure( XMLDoc* pDoc, 
										 const String& sParentQuery, 
										 uint8* pBuff, 
										 uint8& nNumBytesUsed )
{
	// Number of Bets
	uint8 nByteVal = pBuff[nNumBytesUsed++];
	if ( !pDoc->AddChildNode( sParentQuery, TEXT("num_bets"), (uint32) nByteVal ) )
	{
		return false;
	}

	// Add options
	if ( !AddOptionBits( pDoc, sParentQuery, pBuff[nNumBytesUsed++] ) )
	{
		return false;
	}

	// score
	nByteVal = pBuff[nNumBytesUsed++];
	if ( !pDoc->AddChildNode( sParentQuery, TEXT("score"), (uint32) nByteVal ) )
	{
		return false;
	}

	// num cards
	uint8 nNumCards = pBuff[nNumBytesUsed++];
	if ( !pDoc->AddChildNode( sParentQuery, TEXT("num_cards"), (uint32) nNumCards ) )
	{
		return false;
	}

	// the actual cards
	for( uint8 i = 0; i < nNumCards; i++ )
	{
		String sText;
		sText.sprintf( TEXT("card_%d"), i );

		uint8 nCardByte = pBuff[nNumBytesUsed++];
		Card c(nCardByte);

		if ( !pDoc->AddChildNode( sParentQuery, sText, c ) )
		{
			return false;
		}
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// AddGameState
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::AddGameState( XMLDoc* pDoc, uint8* pBuff )
{
	uint8 nBytesUsed = 0;

	if ( !pDoc->AddRootChildNode( TEXT("game_state") ) )
	{
		return false;
	}

	// Hard Access
	uint8 nByteVal = pBuff[nBytesUsed++];
	if ( !pDoc->AddChildNode( TEXT("//game_state"), TEXT("hard_aces"), (bool8)(nByteVal == 1) ) )
	{
		return false;
	}

	// Dealer Hand
	if ( !pDoc->AddChildNode( TEXT("//game_state"), TEXT("dealer_hand") ) )
	{
		return false;
	}
	
	if ( !AddHandStructure( pDoc, TEXT("//dealer_hand"), pBuff, nBytesUsed ) )
	{
		return false;
	}

	// Max Bets
	nByteVal = pBuff[nBytesUsed++];
	if ( !pDoc->AddChildNode( TEXT("//game_state"), TEXT("max_bets"), (uint32) nByteVal ) )
	{
		return false;
	}

	// NBets
	uint8 nBets = pBuff[nBytesUsed++];
	if ( !pDoc->AddChildNode( TEXT("//game_state"), TEXT("nbets"), (uint32) nBets ) )
	{
		return false;
	}

	// Current Bet
	nByteVal = pBuff[nBytesUsed++];
	if ( !pDoc->AddChildNode( TEXT("//game_state"), TEXT("current_bet"), (uint32) nByteVal ) )
	{
		return false;
	}

	// Add Bet States
	for( uint8 i = 0; i < nBets; i++ )
	{
		// Add node for each bet state
		String sText;
		sText.sprintf( TEXT("bet_%d"), i );

		if ( !pDoc->AddChildNode( TEXT("//game_state"), sText ) )
		{
			return false;
		}

		String sQuery = TEXT("//");
		sQuery += sText;

		// Add ibet
		if ( !pDoc->AddChildNode( sQuery, TEXT("ibet"), (uint32) pBuff[nBytesUsed++] ) )
		{
			return false;
		}

		// Original Bet
		int32 nValue;
		Endian::ReceiveInt4( &nValue, pBuff + nBytesUsed );
		if ( !pDoc->AddChildNode( sQuery, TEXT("original_bet"), (uint32) nValue ) )
		{
			return false;
		}
		nBytesUsed += 4;

		// Insurance state
		uint8 nInsState = pBuff[nBytesUsed++]; 
		switch( nInsState )
		{
			case 'N':
				if ( !pDoc->AddChildNode( sQuery, TEXT("insurance_state"), TEXT("No") ) )
				{
					return false;
				}
				break;

			case 'O':
				if ( !pDoc->AddChildNode( sQuery, TEXT("insurance_state"), TEXT("Offered") ) )
				{
					return false;
				}
				break;

			case 'T':
				if ( !pDoc->AddChildNode( sQuery, TEXT("insurance_state"), TEXT("Taken") ) )
				{
					return false;
				}
				break;

			case 'D':
				if ( !pDoc->AddChildNode( sQuery, TEXT("insurance_state"), TEXT("Declined") ) )
				{
					return false;
				}
				break;

			case 'W':
				if ( !pDoc->AddChildNode( sQuery, TEXT("insurance_state"), TEXT("Won") ) )
				{
					return false;
				}
				break;

			case 'L':
				if ( !pDoc->AddChildNode( sQuery, TEXT("insurance_state"), TEXT("Lost") ) )
				{
					return false;
				}
				break;

			default:
				Log::msgError( "Error, Invalid insurance state (%c) received in game state.", nInsState );
				return false;
		}

		// Current Player Hand
		nByteVal = pBuff[nBytesUsed++];
		if ( !pDoc->AddChildNode( sQuery, TEXT("current_player_hand"), (uint32) nByteVal ) )
		{
			return false;
		}

		//  Number of hands
		uint8 nNumHands = pBuff[nBytesUsed++];
		if ( !pDoc->AddChildNode( sQuery, TEXT("num_hands"), (uint32) nNumHands ) )
		{
			return false;
		}

		// Now, add each hand
		for( uint8 j = 0; j < nNumHands; j++ )
		{
			// Add node for each bet state
			// CSN-9699 - Added bet index to hand node name
			String sText;
			sText.sprintf( TEXT("hand_%d_%d"), i, j );

			if ( !pDoc->AddChildNode( sQuery, sText ) )
			{
				return false;
			}

			String sHand = TEXT("//");
			sHand += sText;

			if ( !AddHandStructure( pDoc, sHand, pBuff, nBytesUsed ) )
			{
				return false;
			}
		}

	}

	return true;
}

//****************************************************************************
// Incoming
//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
// TranslateInLogin
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateInLogin( XMLDoc* pDoc, uint8* pGameInBuff, uint32 nGameInBuffSize )
{
	////////////////////////////////////////////////////////////////
	// Login the user
	////////////////////////////////////////////////////////////////
    if ( !LoginUser( pDoc ) )
    {
        return false;
    }

	// Start by skipping the first byte of the message, that will be the
	//  length
	uint32 nOffset = 1;

	////////////////////////////////////////////////////////////////
	// Write Login for BJ
	////////////////////////////////////////////////////////////////
	pGameInBuff[nOffset++] = 'M';
	pGameInBuff[nOffset++] = 'L';
	pGameInBuff[nOffset++] = 'B';
	pGameInBuff[nOffset++] = 'J';

	////////////////////////////////////////////////////////////////
	// Add Version Info
	////////////////////////////////////////////////////////////////
	if ( !AddLoginVersionInfo( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////
	// Add the Machine ID
	////////////////////////////////////////////////////////////////
	if ( !AddLoginMachID( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////
	// Add ID
	////////////////////////////////////////////////////////////////
	if ( !AddLoginID( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}
    
	////////////////////////////////////////////////////////////////
	// play for money/play for fun
	////////////////////////////////////////////////////////////////
	if ( !AddLoginMoneyType( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////
	// Cient type: CSN-7534
	////////////////////////////////////////////////////////////////
	if ( !AddClientID( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}

	////////////////////////////////////////////////////////////////
	// Authentication token. CSN-7075: New Authentication
	////////////////////////////////////////////////////////////////
	if ( !AddAuthenticationToken( nOffset, pDoc, pGameInBuff ) )
	{
		return false;
	}

    ////////////////////////////////////////////////////////////////
	// Finally, add message size
	////////////////////////////////////////////////////////////////
	pGameInBuff[0] = (uint8) nOffset;

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInDeal
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateInDeal( XMLDoc* pDoc, uint8* pGameInBuff, uint32 nGameInBuffSize )
{

	uint8 nMsgSize = 1;

	pGameInBuff[nMsgSize++] = 'D';

	// Max Bets
	uint8 nMaxBets;
	if ( !pDoc->GetSingleNodeByte( TEXT("//bets"), nMaxBets ) )
	{
		return false;
	}
	pGameInBuff[nMsgSize++] = nMaxBets;

	// Store each bet
	for( uint8 i = 0; i < nMaxBets; i++ )
	{
		String sNode;
		sNode.sprintf( TEXT("//bet_%d"), i );

		uint32 nBet;
		if ( !pDoc->GetSingleNodeLong( sNode, nBet ) )
		{
			return false;
		}

		Endian::SendInt4( nBet, pGameInBuff + nMsgSize );
		nMsgSize +=4 ;
	}

	// Finally, write the size of the message
	pGameInBuff[0] = nMsgSize;

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInAction
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateInAction( XMLDoc* pDoc, uint8* pGameInBuff, uint32 nGameInBuffSize )
{
	pGameInBuff[0] = 2;
	pGameInBuff[1] = 'A';

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInHit
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateInHit( XMLDoc* pDoc, uint8* pGameInBuff, uint32 nGameInBuffSize )
{
	pGameInBuff[0] = 2;
	pGameInBuff[1] = 'H';

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInStand
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateInStand( XMLDoc* pDoc, uint8* pGameInBuff, uint32 nGameInBuffSize )
{
	pGameInBuff[0] = 2;
	pGameInBuff[1] = 'S';

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInDouble
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateInDouble( XMLDoc* pDoc, uint8* pGameInBuff, uint32 nGameInBuffSize )
{
	pGameInBuff[0] = 2;
	pGameInBuff[1] = 'B';

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInSplit
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateInSplit( XMLDoc* pDoc, uint8* pGameInBuff, uint32 nGameInBuffSize )
{
	pGameInBuff[0] = 2;
	pGameInBuff[1] = 'P';

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInInsurance
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateInInsurance( XMLDoc* pDoc, uint8* pGameInBuff, uint32 nGameInBuffSize )
{
	uint8 nMsgSize = 1;

	// Set Insurance character
	pGameInBuff[nMsgSize++] = 'I';

	// Get the amount
	uint32 nAmt;
	if ( !pDoc->GetSingleNodeLong( TEXT("//amount"), nAmt ) )
	{
		return false;
	}
	Endian::SendInt4( nAmt, pGameInBuff + nMsgSize );
	nMsgSize +=4 ;

	// Get the bet number
	uint8 nBet;
	if ( !pDoc->GetSingleNodeByte( TEXT("//bet_num"), nBet ) )
	{
		return false;
	}
	pGameInBuff[nMsgSize++] = nBet;

	// Finally, put the message size in
	pGameInBuff[0] = nMsgSize;

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateInSurrender
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateInSurrender( XMLDoc* pDoc, uint8* pGameInBuff, uint32 nGameInBuffSize )
{
	pGameInBuff[0] = 2;
	pGameInBuff[1] = 'R';

	return true;
}

/// [2013-07-09] Charles. Added for supporting 'Zappit Rackcard'.
//////////////////////////////////////////////////////////////////////////////
// TranslateInZap
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateInZap( XMLDoc* pDoc, uint8* pGameInBuff, uint32 nGameInBuffSize )
{
	pGameInBuff[0] = 2;
	pGameInBuff[1] = 'Z';

	return true;
}

//****************************************************************************
// Outgoing
//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
// TranslateOutError
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutError( uint8* pOutBuff, 
										  uint32 nOutBuffSize, 
										  uint8* pGameOutBuff, 
										  uint32 nGameOutBuffSize )
{
	bool8 bRetVal = false;

	if ( pGameOutBuff[1] == '$' )
	{
		bRetVal = ReturnErrorMessage( TEXT("InsufficientFunds"), pOutBuff, nOutBuffSize );
	}
	else if ( pGameOutBuff[1] == 'G' )
	{
		bRetVal = ReturnErrorMessage( TEXT("InProgress"), pOutBuff, nOutBuffSize );
	}
	else if ( pGameOutBuff[1] == 'N' )
	{
        // kharmon.  02-21-07.  MCA #29819.  Need to send back different messages for auto and admin logoffs.
        String strErrorName = "InvalidLogin";

        if(pGameOutBuff[0]==6)  // The extra 4 bytes have the error code.
        {
            int32 nErrorCode=0;
            Endian::ReceiveInt4( &nErrorCode, &pGameOutBuff[2] );

            switch(nErrorCode)
            {
                case TrackSQL::AdminLogout:
                {
                    strErrorName = "AdminLogout";
                    break;
                }
                case TrackSQL::AutoLogout:
                {
                    strErrorName = "TimedOut";
                    break;
                }
            }            
        }

        bRetVal = ReturnErrorMessage( strErrorName, pOutBuff, nOutBuffSize );
	}
	else if ( pGameOutBuff[1] == 'b' )
	{
		bRetVal = ReturnErrorMessage( TEXT("InvalidBet"), pOutBuff, nOutBuffSize );
	}
	else if ( pGameOutBuff[1] == 'l' )
	{
		bRetVal = ReturnErrorMessage( TEXT("NoLogin"), pOutBuff, nOutBuffSize );
	}
	else if ( pGameOutBuff[1] == '!' )
	{
		bRetVal = ReturnErrorMessage( TEXT("Shutdown"), pOutBuff, nOutBuffSize );
	}
	else if ( pGameOutBuff[1] == 'C' )
	{
		bRetVal = ReturnErrorMessage( TEXT("SettingsChanged"), pOutBuff, nOutBuffSize );
	}

	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutLogin
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutLogin( uint8* pOutBuff, 
										  uint32 nOutBuffSize, 
										  uint8* pGameOutBuff, 
										  uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Login") ) )
	{
		return false;
	}

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff+2 );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}

	// Minimum Bet
	Endian::ReceiveInt4( &nValue, pGameOutBuff+6 );
	if ( !doc.AddRootChildNode( TEXT("min_bet"), (uint32) nValue ) )
	{
		return false;
	}

	// Maximum Bet
	Endian::ReceiveInt4( &nValue, pGameOutBuff+10 );
	if ( !doc.AddRootChildNode( TEXT("max_bet"), (uint32) nValue ) )
	{
		return false;
	}

	// Number of splits allowed
	uint8 nByteVal = *((uint8*)pGameOutBuff+14);
	if ( !doc.AddRootChildNode( TEXT("split_allow"), (uint32) nByteVal ) )
	{
		return false;
	}

	// Save Game?
	nByteVal = *((uint8*)pGameOutBuff+15);
	bool8 bSaveGame = (nByteVal == 1);
	if ( !doc.AddRootChildNode( TEXT("saved"), bSaveGame ) )
	{
		return false;
	}

	// If we have a saved game, we need to add that part
	if ( bSaveGame )
	{
		// add the game state
		if ( !AddGameState( &doc, pGameOutBuff + 16 ) )
		{
			return false;
		}
	}

	if ( !AddCurrencyOutput(&doc) )	// CSN-11204: return currency
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutOptions
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutOptions( uint8* pOutBuff, 
									        uint32 nOutBuffSize, 
											uint8* pGameOutBuff, 
											uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	// We are expecting a 4 byte options message!
	ASSERT( pGameOutBuff[0] == 4 );

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Options") ) )
	{
		return false;
	}

	// Start Adding Options
	if ( !AddOptionBits( &doc, pGameOutBuff[2] ) )
	{
		return false;
	}

	// Now add max bets
	uint8 nMaxBets = pGameOutBuff[3];
	if ( !doc.AddRootChildNode( TEXT("max_bets"), (uint32) nMaxBets ) )
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutAction
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutAction( uint8* pOutBuff, 
									       uint32 nOutBuffSize, 
										   uint8* pGameOutBuff, 
										   uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	// We are expecting a 5 byte action message!
	ASSERT( pGameOutBuff[0] == 5 );

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Action") ) )
	{
		return false;
	}

	// Start Adding Options
	if ( !AddOptionBits( &doc, pGameOutBuff[2] ) )
	{
		return false;
	}

	// Now add Insurance
	if ( !doc.AddRootChildNode( TEXT("insurance"), (bool8)(pGameOutBuff[3] == 1) ) )
	{
		return false;
	}
	
	// Now add no-Insurance
	if ( !doc.AddRootChildNode( TEXT("noinsurance"), (bool8)(pGameOutBuff[4] == 1) ) )
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutHit
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutHit( uint8* pOutBuff, 
									    uint32 nOutBuffSize, 
									    uint8* pGameOutBuff, 
										uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	// We are expecting a 8 byte hit message!
	ASSERT( pGameOutBuff[0] == 8 );

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Hit") ) )
	{
		return false;
	}

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff+2 );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}

	// Card
	uint8 nByteVal = *((uint8*)pGameOutBuff+6);
	Card c(nByteVal);
	if ( !doc.AddRootChildNode( TEXT("card"), c ) )
	{
		return false;
	}

	// Score
	nByteVal = *((uint8*)pGameOutBuff+7);
	if ( !doc.AddRootChildNode( TEXT("score"), (uint32) nByteVal ) )
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutStand
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutStand( uint8* pOutBuff, 
									      uint32 nOutBuffSize, 
									      uint8* pGameOutBuff, 
									 	  uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	// We are expecting a 6 byte stand message!
	ASSERT( pGameOutBuff[0] == 6 );

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Stand") ) )
	{
		return false;
	}

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff+2 );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}

	// Busted
	if ( !doc.AddRootChildNode( TEXT("busted"), (bool8)(pGameOutBuff[1] == 's') ) )
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutStand
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutSurrender( uint8* pOutBuff, 
									          uint32 nOutBuffSize, 
									          uint8* pGameOutBuff, 
									 	      uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	// [AO 2007-09-10] We are expecting a 10 byte surrender message in the following format:
	// [length: byte], 'R', [balance: dword], [payout: dword].  Added processing the "payout" field.
	ASSERT(pGameOutBuff[0] == 10);

	if (!doc.StartNew())
		return false;

	if (!doc.CreateRootNode(TEXT("game_msg")))
		return false;

	if (!doc.AddRootChildNode(TEXT("msg_id"), TEXT("Surrender")))
		return false;

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4(&nValue, pGameOutBuff + 2);
	if (!doc.AddRootChildNode(TEXT("balance"), static_cast<uint32>(nValue)))
		return false;

	// [AO 2007-09-10] Payout
	Endian::ReceiveInt4(&nValue, pGameOutBuff + 6);
	if (!doc.AddRootChildNode(TEXT("payout"), static_cast<uint32>(nValue)))
		return false;

	// Done - send the actual XML
	if (!doc.SendXML(pOutBuff, nOutBuffSize))
		return false;

	return true;
}

/// [2013-07-09] Charles. Added for supporting 'Zappit Rackcard'.
//////////////////////////////////////////////////////////////////////////////
// TranslateOutZap
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutZap( uint8* pOutBuff, 
									   uint32 nOutBuffSize, 
									   uint8* pGameOutBuff, 
									   uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Zap") ) )
	{
		return false;
	}

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff+2 );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}

	// Cards
	for( uint8 i=0; i<2; i++) {
		String sText;
		sText.sprintf( TEXT("card_%d"), i );

		uint8 nCardByte = *((uint8*)pGameOutBuff+6+i);
		Card c(nCardByte);
		if ( !doc.AddRootChildNode( sText, c ) ) {
			return false;
		}
	}

	// Score
	uint8 nByteVal = *((uint8*)pGameOutBuff+8);
	if ( !doc.AddRootChildNode( TEXT("score"), (uint32) nByteVal ) )
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutDeal
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutDeal( uint8* pOutBuff, 
									     uint32 nOutBuffSize, 
									     uint8* pGameOutBuff, 
									     uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Deal") ) )
	{
		return false;
	}

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff+2 );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}

	// Build deal script
	uint8 nDealScriptEntries = pGameOutBuff[6];

	String sScript;
	for( uint8 i = 0; i < nDealScriptEntries; i++ )
	{
		sScript += String::Character(pGameOutBuff[7+i]);
	}

	if ( !doc.AddRootChildNode( TEXT("script"), sScript ) )
	{
		return false;
	}

	// add the game state
	if ( !AddGameState( &doc, pGameOutBuff + 7 + nDealScriptEntries ) )
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutDouble
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutDouble( uint8* pOutBuff, 
									       uint32 nOutBuffSize, 
									       uint8* pGameOutBuff, 
									       uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	// Expecting 8 byte double response
	ASSERT( pGameOutBuff[0] == 8 );

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Double") ) )
	{
		return false;
	}

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff+2 );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}

	// New Card
	uint8 nCardByte = pGameOutBuff[6];
	Card c(nCardByte);

	if ( !doc.AddRootChildNode( TEXT("card"), c ) )
	{
		return false;
	}

	// Score
	uint8 nByteVal = pGameOutBuff[7];
	if ( !doc.AddRootChildNode( TEXT("score"), (uint32) nByteVal ) )
	{
		return false;
	}	

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutSplit
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutSplit( uint8* pOutBuff, 
									      uint32 nOutBuffSize, 
									      uint8* pGameOutBuff, 
									      uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Split") ) )
	{
		return false;
	}

	// Balance 
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff+2 );
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}

	// add the game state
	if ( !AddGameState( &doc, pGameOutBuff + 6 ) )
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutInsurance
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutInsurance( uint8* pOutBuff, 
									          uint32 nOutBuffSize, 
									          uint8* pGameOutBuff, 
									          uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Insurance") ) )
	{
		return false;
	}

	switch( pGameOutBuff[2] )
	{
		case 'T':
		{
			// Result
			if ( !doc.AddRootChildNode( TEXT("result"), TEXT("Taken") ) )
			{
				return false;
			}

			// Bet amount 
			int32 nValue;
			Endian::ReceiveInt4( &nValue, pGameOutBuff+3 );
			if ( !doc.AddRootChildNode( TEXT("amount"), (uint32) nValue ) )
			{
				return false;
			}	

			// Balance 
			Endian::ReceiveInt4( &nValue, pGameOutBuff+7 );
			if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
			{
				return false;
			}	

			// ibet
			uint8 nByteVal = pGameOutBuff[11];
			if ( !doc.AddRootChildNode( TEXT("ibet"), (uint32) nByteVal ) )
			{
				return false;
			}				
			break;
		}

		case 'D':
		{
			// Result
			if ( !doc.AddRootChildNode( TEXT("result"), TEXT("Declined") ) )
			{
				return false;
			}

			// Bet amount 
			int32 nValue;
			Endian::ReceiveInt4( &nValue, pGameOutBuff+3 );
			if ( !doc.AddRootChildNode( TEXT("amount"), (uint32) nValue ) )
			{
				return false;
			}	

			// Balance 
			Endian::ReceiveInt4( &nValue, pGameOutBuff+7 );
			if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
			{
				return false;
			}	

			// ibet
			uint8 nByteVal = pGameOutBuff[11];
			if ( !doc.AddRootChildNode( TEXT("ibet"), (uint32) nByteVal ) )
			{
				return false;
			}				
			break;
		}
	
		case 'L':
		{
			// Result
			if ( !doc.AddRootChildNode( TEXT("result"), TEXT("Lost") ) )
			{
				return false;
			}

			// Payout 
			int32 nValue;
			Endian::ReceiveInt4( &nValue, pGameOutBuff+3 );
			if ( !doc.AddRootChildNode( TEXT("payout"), (uint32) nValue ) )
			{
				return false;
			}	

			// Balance 
			Endian::ReceiveInt4( &nValue, pGameOutBuff+7 );
			if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
			{
				return false;
			}	

			// ibet
			uint8 nByteVal = pGameOutBuff[11];
			if ( !doc.AddRootChildNode( TEXT("ibet"), (uint32) nByteVal ) )
			{
				return false;
			}				
			break;
		}

		case 'W':
		{
			// Result
			if ( !doc.AddRootChildNode( TEXT("result"), TEXT("Won") ) )
			{
				return false;
			}

			// Payout 
			int32 nValue;
			Endian::ReceiveInt4( &nValue, pGameOutBuff+3 );
			if ( !doc.AddRootChildNode( TEXT("payout"), (uint32) nValue ) )
			{
				return false;
			}	

			// Balance 
			Endian::ReceiveInt4( &nValue, pGameOutBuff+7 );
			if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
			{
				return false;
			}	

			// ibet
			uint8 nByteVal = pGameOutBuff[11];
			if ( !doc.AddRootChildNode( TEXT("ibet"), (uint32) nByteVal ) )
			{
				return false;
			}		
			
			// Dealer Hand
			if ( !doc.AddRootChildNode( TEXT("dealer_hand") ) )
			{
				return false;
			}

			uint8 nBytesUsed = 12;
			if ( !AddHandStructure( &doc, TEXT("//dealer_hand"), pGameOutBuff, nBytesUsed ) )
			{
				return false;
			}			
			break;
		}

		default:
			Log::msgError( "Error, received unknown server insurance msg: %c", pGameOutBuff[2] );
			return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutPlayNext
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutPlayNext( uint8* pOutBuff, 
									         uint32 nOutBuffSize, 
									         uint8* pGameOutBuff, 
									         uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("PlayNextBet") ) )
	{
		return false;
	}

	// current bet
	uint8 nByteVal = pGameOutBuff[2];
	if ( !doc.AddRootChildNode( TEXT("cur_bet"), (uint32) nByteVal ) )
	{
		return false;
	}	

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////////
// TranslateOutFinished
//////////////////////////////////////////////////////////////////////////////
bool8 BJXMLTranslator::TranslateOutFinished( uint8* pOutBuff, 
									         uint32 nOutBuffSize, 
									         uint8* pGameOutBuff, 
									         uint32 nGameOutBuffSize )
{
	XMLDoc doc;

	if ( !doc.StartNew() )
	{
		return false;
	}

	if ( !doc.CreateRootNode( TEXT("game_msg") ) )
	{
		return false;
	}

	if ( !doc.AddRootChildNode( TEXT("msg_id"), TEXT("Finished") ) )
	{
		return false;
	}

	// Skip past message length and id character
	uint8 nBytesUsed = 2;
			
	// Dealer Hand
	if ( !doc.AddRootChildNode( TEXT("dealer_hand") ) )
	{
		return false;
	}
	if ( !AddHandStructure( &doc, TEXT("//dealer_hand"), pGameOutBuff, nBytesUsed ) )
	{
		return false;
	}	
	
	// NBets
	uint8 nBets = pGameOutBuff[nBytesUsed++];
	if ( !doc.AddRootChildNode( TEXT("nbets"), (uint32) nBets ) )
	{
		return false;
	}

	// Add Bet States
	for( uint8 i = 0; i < nBets; i++ )
	{
		// Add node for each bet state
		String sText;
		sText.sprintf( TEXT("bet_%d"), i );
		
		if ( !doc.AddRootChildNode( sText ) )
		{
			return false;
		}

		String sQuery = TEXT("//");
		sQuery += sText;

		// Add ibet
		if ( !doc.AddChildNode( sQuery, TEXT("ibet"), (uint32) pGameOutBuff[nBytesUsed++] ) )
		{
			return false;
		}
		
		// Add number of hands
		uint8 nHands = pGameOutBuff[nBytesUsed++];
		if ( !doc.AddChildNode( sQuery, TEXT("num_hands"), (uint32) nHands ) )
		{
			return false;
		}

		// Add each hand payout info
		for( uint8 j = 0; j < nHands; j++ )
		{
			// Payout info for each hand
			// CSN-9699 - Added bet index to hand node name
			sText.sprintf( TEXT("hand_%d_%d"), i, j );
			if ( !doc.AddChildNode( sQuery, sText ) )
			{
				return false;
			}

			String sHandQuery = TEXT("//");
			sHandQuery += sText;

			// Add payout
			int32 nValue;
			Endian::ReceiveInt4( &nValue, pGameOutBuff + nBytesUsed );
			nBytesUsed += 4;
			if ( !doc.AddChildNode( sHandQuery, TEXT("payout"), (uint32) nValue ) )
			{
				return false;
			}

			// Add bonus
			nValue;
			Endian::ReceiveInt4( &nValue, pGameOutBuff + nBytesUsed );
			nBytesUsed += 4;
			if ( !doc.AddChildNode( sHandQuery, TEXT("bonus"), (uint32) nValue ) )
			{
				return false;
			}

			// bonus name
			uint8 nBonusNameLen = pGameOutBuff[nBytesUsed++];
			char szBonusName[256];

			if ( nBonusNameLen > 0 )
			{
				strncpy( szBonusName, (char*) pGameOutBuff + nBytesUsed, 255 );
				szBonusName[nBonusNameLen] = 0;
			}
			else
			{
				szBonusName[0] = 0;
			}
			
			nBytesUsed += nBonusNameLen;
			if ( !doc.AddChildNode( sHandQuery, TEXT("bonus_name"), szBonusName ) )
			{
				return false;
			}
		}
	}

	// Dealer Payout Info

	// Add payout
	int32 nValue;
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nBytesUsed );
	nBytesUsed += 4;
	if ( !doc.AddRootChildNode( TEXT("dealer_payout"), (uint32) nValue ) )
	{
		return false;
	}

	// Add bonus
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nBytesUsed );
	nBytesUsed += 4;
	if ( !doc.AddRootChildNode( TEXT("dealer_bonus"), (uint32) nValue ) )
	{
		return false;
	}

	// bonus name
	uint8 nBonusNameLen = pGameOutBuff[nBytesUsed++];
	char szBonusName[256];

	if ( nBonusNameLen > 0 )
	{
		strncpy( szBonusName, (char*) pGameOutBuff + nBytesUsed, 255 );
		szBonusName[nBonusNameLen] = 0;
	}
	else
	{
		szBonusName[0] = 0;
	}
	
	nBytesUsed += nBonusNameLen;
	if ( !doc.AddRootChildNode( TEXT("dealer_bonus_name"), szBonusName ) )
	{
		return false;
	}

	// Add player balance
	Endian::ReceiveInt4( &nValue, pGameOutBuff + nBytesUsed );
	nBytesUsed += 4;
	if ( !doc.AddRootChildNode( TEXT("balance"), (uint32) nValue ) )
	{
		return false;
	}

	// Done - send the actual XML
	if ( !doc.SendXML( pOutBuff, nOutBuffSize ) )
	{
		return false;
	}

	return true;
}